package model;
import java.util.Random;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.awt.*;
import view.HistogramView;
import view.SimulatorView;

/**
 * A predator-prey simulator, based on a rectangular field
 * containing rabbits, foxes, bears and hunters.
 * 
 * @author Martin Kromkamp and Albert-Jan Boer.
 * @version 2009.01.28
 */
public class Simulator implements Runnable 
{
    // The current state of the field.
    private Field field;
    // The current step of the simulation.
    private int step;
    // Histogram
    private HistogramView histogram;
    // A graphical view of the simulation.
    private SimulatorView view;
    // Values
    private Values value;
    // depth and width
    private int depth, width;
    
    
        
    /**
     * Create a simulation field with the given size.
     * @param depth Depth of the field. Must be greater than zero.
     * @param width Width of the field. Must be greater than zero.
     */
    public Simulator(int depth, int width, Values values)
    {
        if(width <= 0 || depth <= 0) {
            System.out.println("The dimensions must be greater than zero.");
            System.out.println("Using default values.");
            this.depth = value.DEFAULT_DEPTH;
            this.width = value.DEFAULT_WIDTH;
        } else {
        	this.depth = depth;
        	this.width = width;
        }
        
        this.value = values;
        
        value.animalsList = new ArrayList<Animal>();
        value.huntersList = new ArrayList<Hunter>();
        field = new Field(depth, width);

        // Create a view of the state of each location in the field.
        view = new SimulatorView(depth, width, this);
        view.setColor(Rabbit.class, Color.orange);
        view.setColor(Fox.class, Color.blue);
        view.setColor(Bear.class, Color.black );
        view.setColor(Hunter.class, Color.red );
        
        value.fieldHistory = new ArrayList<Field>();
        
        // If histogram chart is enabled show it.
        if( value.showHistogram ) {
        	 histogram = new HistogramView(310, 660, depth * width, field, value);
        }

        // Setup a valid starting point.
        reset();
    }
    
        
    /**
     * Run the simulation from its current state for a reasonably long period,
     * e.g. 500 steps.
     */
    public void runLongSimulation()
    {
        simulate(500);
    }
    
    /**
     * Run the simulation from its current state for the given number of steps.
     * Stop before the given number of steps if it ceases to be viable.
     * @param numSteps The number of steps to run for.
     */
    public void simulate(int numSteps)
    {
        for(int step = 1; step <= numSteps && view.isViable(field); step++) {
        	simulateOneStep();
        }
    }
    
    /**
     * Run the simulation from its current state for a single step.
     * Iterate over the whole field updating the state of each
     * animal and hunter.
     */
    public void simulateOneStep()
    {
    	
        step++;
        // Provide space for newborn animals.
        List<Animal> newAnimals = new ArrayList<Animal>(); 
        // Let all rabbits act.
        for(Iterator<Animal> it = value.animalsList.iterator(); it.hasNext(); ) {
            Animal animal = it.next();
            animal.act(newAnimals);
            if(! animal.isAlive()) {
                it.remove();
            }
        }
        
        for(Iterator<Hunter> it = value.huntersList.iterator(); it.hasNext(); ) {
        	Hunter hunter = it.next();
        	hunter.act(newAnimals);
        }
        
               
        // Add the newly born foxes and rabbits to the main lists.
        value.animalsList.addAll(newAnimals);
        
        view.showStatus(step, field);
        // Update the histogram
        if( value.showHistogram ) {
        	histogram.updateHistogram( field );
        }
        // Add values to fieldHistory
        value.fieldHistory.add( field );
    }

        
    /**
     * Reset the simulation to a starting position.
     */
    public void reset()
    {
        step = 0;
        value.animalsList.clear();
        value.huntersList.clear();
        populate();
        
        // Show the starting state in the view.
        view.showStatus(step, field);
    }
    
    /**
     * Randomly populate the field with animals and hunters.
     */
    private void populate()
    {
        Random rand = Randomizer.getRandom();
        field.clear();
        int i = 0;
        for(int row = 0; row < field.getDepth(); row++) {
            for(int col = 0; col < field.getWidth(); col++) {
            	if( rand.nextDouble() <= value.HUNTER_CREATION_PROBABILITY ) {
                	Location location = new Location( row, col );
                	Hunter hunter = new Hunter( true, field, location, value);
                	value.huntersList.add( hunter );
                	i++;
            	}
            	else if(rand.nextDouble() <= value.BEAR_CREATION_PROBABILITY) {
                    Location location = new Location(row, col);
                    Bear bear = new Bear(true, field, location, value);
                    value.animalsList.add(bear);
                }
            	else if(rand.nextDouble() <= value.FOX_CREATION_PROBABILITY) {
                    Location location = new Location(row, col);
                    Fox fox = new Fox(true, field, location, value);
                    value.animalsList.add(fox);
                }
                else if(rand.nextDouble() <= value.RABBIT_CREATION_PROBABILITY) {
                    Location location = new Location(row, col);
                    Rabbit rabbit = new Rabbit(true, field, location, value);
                    value.animalsList.add(rabbit);
                } 
                
                // Else leave the location empty.
            }
        }
        value.fieldHistory.add( field );
    }


	@Override
	
	/**
     * Run the simulation.
     */
	public void run() {
		
			for(int step = 1; step <= value.NUMSTEPS && view.isViable(field); step++) {
				simulateOneStep();
				try {
					Thread.sleep( 100 );
				} catch (InterruptedException e) {}            
	        }
			// Makes a beep when the simulation is done.
			Toolkit.getDefaultToolkit().beep();
		} 
}

